use std::collections::{HashMap, HashSet};

use super::deadcode::*;
use super::reload::*;

use crate::frontend::scopes::{Labels};
use crate::llvm::*;
use crate::config::*;

#[allow(unused)]
pub fn eliminate_bb(mut program: LLVMProgram) -> LLVMProgram {
    program.func_def = program.func_def.into_iter()
        .map(|f| {
            let active_bb = unreachable_eliminate::eliminate(&f);
            let mut labels = Labels::new();
            let mut label_map = HashMap::new();
            f.reload(
                &mut |s|{
                    update_label(&mut labels, &mut label_map, s)
                },
                &|s| {
                    active_bb.contains(s)
                },
                &|_|{
                    true
                })
        })
        .collect();

    program
}

#[allow(unused)]
pub fn eliminate_all(mut program: LLVMProgram) -> LLVMProgram {
    vprintln!("Eliminating unreachable basic block and deadcode...");
    program.func_def = program.func_def.into_iter()
        .map(|f| {
            let active_bb = unreachable_eliminate::eliminate(&f);
            let active_labels = dead_code_eliminate::eliminate(&f, &active_bb);
            let instr_cnt = f.count_instr();

            let mut active_instrs: HashSet<usize> = f.blocks.iter()
                .map(|b| {
                    let mut res: HashSet<usize> = HashSet::new();
                    let mut instr_cnt = b.instr_cnt;
                    for p in b.phi_instr.iter() {
                        if p.is_active(&active_labels) {
                            res.insert(instr_cnt);
                        }
                        instr_cnt += 1;
                    }
                    for i in b.instrs.iter() {
                        if i.is_active(&active_labels) {
                            res.insert(instr_cnt);
                        }
                        instr_cnt += 1;
                    }
                    if b.ter_instr.is_some() {
                        res.insert(instr_cnt);
                    }
                    res
                })
                .fold(HashSet::new(), |mut acc, instrs| {
                    for i in instrs.into_iter() {
                        acc.insert(i);
                    }
                    acc
                });
    
            let mut active_allocs: HashSet<usize> = f.allocs.iter()
                .enumerate()
                .filter(|(_, (a, _))| a.is_active(&active_labels))
                .map(|(i, _)| instr_cnt+i)
                .collect();

            active_allocs.into_iter()
                .for_each(|i| { active_instrs.insert(i); });

            let mut labels = Labels::new();
            let mut label_map = HashMap::new();
            f.reload(
                &mut |s|{
                    update_label(&mut labels, &mut label_map, s)
                }, 
                &|s| {
                    active_bb.contains(s)
                },
                &|i| {
                    active_instrs.contains(&(i as usize))
                })
        })
        .collect();

    vprintln!("Finished\n");
    program
}

#[allow(unused)]
pub fn debug_elim_bb(program: LLVMProgram) -> LLVMProgram {
    for f in program.func_def.iter() {
        unreachable_eliminate::debug(&f);
    }
    program
}

#[allow(unused)]
pub fn debug_elim_instr(program: LLVMProgram) -> LLVMProgram {
    for f in program.func_def.iter() {
        let active_bb = unreachable_eliminate::eliminate(&f);
        dead_code_eliminate::debug(&f, &active_bb);
    }
    program
}


